/**
 * libpsd - Photoshop file formats (*.psd) decode library
 * Copyright (C) 2004-2007 Graphest Software.
 *
 * libpsd is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: psd.c, created by Patrick in 2006.05.18, libpsd@graphest.com Exp $
 */

#include "libpsd.h"
#include "psd_config.h"
#include "psd_system.h"
#include "psd_stream.h"


enum {
    PSD_FILE_HEADER,
    PSD_COLOR_MODE_DATA,
    PSD_IMAGE_RESOURCE,
    PSD_LAYER_AND_MASK_INFORMATION,
    PSD_IMAGE_DATA,
    PSD_DONE
};

extern psd_status psd_get_file_header(psd_context * context);
extern psd_status psd_get_color_mode_data(psd_context * context);
extern psd_status psd_get_image_resource(psd_context * context);
extern psd_status psd_get_layer_and_mask(psd_context * context);
extern psd_status psd_get_image_data(psd_context * context);
extern void psd_color_mode_data_free(psd_context * context);
extern void psd_image_resource_free(psd_context * context);
extern void psd_layer_and_mask_free(psd_context * context);
extern void psd_image_data_free(psd_context * context);
extern void psd_image_blend_free(psd_context * context);

static psd_file_ops psd_std_fileops = {
    psd_std_size,
    psd_std_seek,
    psd_std_read,
//    psd_std_write,
//    psd_std_close,
//    psd_std_gets,
//    psd_std_eof,
//    psd_std_tell,
//    psd_std_getc,
//    psd_std_scanf
};

static psd_status psd_image_load_tag(psd_context ** dst_context, psd_char * file_name, psd_load_tag load_tag)
{
    psd_context * context;
    psd_status status;

    if(dst_context == NULL)
        return psd_status_invalid_context;
    if(file_name == NULL)
        return psd_status_invalid_file;

    context = (psd_context *)psd_malloc(sizeof(psd_context));
    if(context == NULL)
        return psd_status_malloc_failed;
    memset(context, 0, sizeof(psd_context));

    context->ops_ = &psd_std_fileops;
    context->file_name = file_name;
    context->file = psd_fopen(file_name);
    if (context->file == NULL)
    {
        psd_free(context);
        return psd_status_invalid_file;
    }
    
    context->state = PSD_FILE_HEADER;
    context->stream.file_length = psd_fsize(context->file);
    context->load_tag = load_tag;
    status = psd_main_loop(context);
    
    if(status != psd_status_done)
    {
        psd_image_free(context);
        context = NULL;
    }
    else
    {
        psd_stream_free(context);
    }
    
    *dst_context = context;

    return status;
}

psd_status psd_image_load(psd_context ** dst_context, psd_char * file_name)
{
    return psd_image_load_tag(dst_context, file_name, psd_load_tag_all);
}

psd_status psd_image_load_header(psd_context ** dst_context, psd_char * file_name)
{
    return psd_image_load_tag(dst_context, file_name, psd_load_tag_header);
}

psd_status psd_image_load_layer(psd_context ** dst_context, psd_char * file_name)
{
    return psd_image_load_tag(dst_context, file_name, psd_load_tag_layer);
}

psd_status psd_image_load_merged(psd_context ** dst_context, psd_char * file_name)
{
    return psd_image_load_tag(dst_context, file_name, psd_load_tag_merged);
}

psd_status psd_image_load_thumbnail(psd_context ** dst_context, psd_char * file_name)
{
    return psd_image_load_tag(dst_context, file_name, psd_load_tag_thumbnail);
}

psd_status psd_image_load_exif(psd_context ** dst_context, psd_char * file_name)
{
#ifdef PSD_INCLUDE_LIBXML
    return psd_image_load_tag(dst_context, file_name, psd_load_tag_exif);
#else
    return psd_status_unsupport_yet;
#endif
}

psd_status psd_image_free(psd_context * context)
{
    if(context == NULL)
        return psd_status_invalid_context;
    
    psd_color_mode_data_free(context);
    psd_image_resource_free(context);
    psd_layer_and_mask_free(context);
    psd_image_data_free(context);
    
    psd_image_blend_free(context);
    psd_stream_free(context);
    
    //psd_free(context); //[DP] the application is responsible for this

    return psd_status_done;
}

psd_static psd_status psd_main_loop(psd_context * context)
{
    psd_status status = psd_status_done;

    while(status == psd_status_done)
    {
        if(context->stream.file_length <= 0)
        {
            status = psd_status_fread_error;
            break;
        }
        
        switch(context->state)
        {
            case PSD_FILE_HEADER:
                status = psd_get_file_header(context);
                if(status == psd_status_done)
                {
                    if (context->load_tag == psd_load_tag_header)
                        context->state = PSD_DONE;
                    else
                        context->state = PSD_COLOR_MODE_DATA;
                }
                else if(status == psd_status_unkown_error)
                    status = psd_status_file_header_error;
                break;
                
            case PSD_COLOR_MODE_DATA:
                status = psd_get_color_mode_data(context);
                if(status == psd_status_done)
                    context->state = PSD_IMAGE_RESOURCE;
                else if(status == psd_status_unkown_error)
                    status = psd_status_color_mode_data_error;
                break;
                
            case PSD_IMAGE_RESOURCE:
                status = psd_get_image_resource(context);
                if(status == psd_status_done)
                    context->state = PSD_LAYER_AND_MASK_INFORMATION;
                else if(status == psd_status_unkown_error)
                    status = psd_status_image_resource_error;
                break;
                
            case PSD_LAYER_AND_MASK_INFORMATION:
                status = psd_get_layer_and_mask(context);
                if(status == psd_status_done)
                    context->state = PSD_IMAGE_DATA;
                else if(status == psd_status_unkown_error)
                    status = psd_status_layer_and_mask_error;
                break;
                
            case PSD_IMAGE_DATA:
                status = psd_get_image_data(context);
                if(status == psd_status_done)
                    context->state = PSD_DONE;
                else if(status == psd_status_unkown_error)
                    status = psd_status_image_data_error;
                break;

            case PSD_DONE:
                return psd_status_done;

            default:
                psd_assert(0);
                return psd_status_unkown_error;
        }
    }

    return status;
}

